import type { Request, Response } from 'express';

import { basename } from 'node:path';
import { renderToString } from 'vue/server-renderer';
import devalue from '@nuxt/devalue';
import { useAppStore } from '@/mobile/stores/app';
import { createApp } from './main';
import {
  COOKIE_KEY_LANGUAGE,
  DEFAULT_LANGUAGE,
  COOKIE_KEY_TOKEN,
  COOKIE_DOMAIN_ATOMGIT,
  COOKIE_KEY_ATOMGIT_ACCESS_TOKEN,
} from '@/common/utils/constants';
import { convertLanguageStringToAtomGit, convertLanguageStringFromAtomGit, isAvailableLanguage } from '@/common/utils/language';
import { getWrappedTitle } from '@/mobile/utils/other';
import { parse as parseUserAgent } from '@/common/utils/user-agent';

export interface AppContext {
  redirect?: string,
  title?: string,
  keywords?: string,
  description?: string,
  modules?: string[],
  httpStatusCode?: number,
}

export interface RenderContext {
  url: string,
  manifest: Record<string, string[]>,
  request: Request,
  response: Response,
}

export async function render({ url, manifest, request, response }: RenderContext) {
  let language = DEFAULT_LANGUAGE;

  // 从 Cookie 中获取当前用户选中的语言
  // 为兼容 atomgit 语言 cookie 内容，此处需要进行变换
  const cookieLanguage = convertLanguageStringFromAtomGit(request.cookies[COOKIE_KEY_LANGUAGE] as string | undefined);

  if (cookieLanguage) {
    if (isAvailableLanguage(cookieLanguage)) {
      language = cookieLanguage;
    }
  }
  else if (request.headers['accept-language']) {
    // 从 header 中获取语言
    const acceptLanguage = request.headers['accept-language']?.split(',')[0];

    if (isAvailableLanguage(acceptLanguage)) {
      language = acceptLanguage;

      // 此处也需要进行语言 cookie 内容的变换
      response.cookie(COOKIE_KEY_LANGUAGE, convertLanguageStringToAtomGit(language), {
        path: '/',
        expires: new Date(Date.now() + 3600000 * 24 * 3650),
        domain: import.meta.env.DEV ? undefined : COOKIE_DOMAIN_ATOMGIT,
      });
    }
  }

  // 处理全局社区名
  let communityName = '';

  if (import.meta.env.VITE_MOCK_COMMUNITY_NAME) {
    communityName = import.meta.env.VITE_MOCK_COMMUNITY_NAME;
  }
  else {
    communityName = request.hostname.split('.').reverse()[2] ?? '';
  }

  // 处理 Token
  let token = request.cookies[COOKIE_KEY_TOKEN] as string ?? '';
  // 处理 AtomGit 自动登录
  if (!token) {
    // 获取 AtomGit 的 Token
    token = request.cookies[COOKIE_KEY_ATOMGIT_ACCESS_TOKEN] as string ?? '';
    token && response.cookie(COOKIE_KEY_TOKEN, token, {
      path: '/',
      expires: new Date(Date.now() + 3600000 * 24 * 30),
      sameSite: 'lax',
    });
  }

  // 获取设备类型
  const userAgent = parseUserAgent(request.headers['user-agent'] ?? '');
  const { app, router, pinia } = createApp({ language, communityName, token, userAgent });

  const store = useAppStore(pinia);
  const [, userIsSuccess] = await Promise.all([
    store.fetchConfigForServer({ language, community: communityName, token }),
    store.fetchUserForServer({ language, community: communityName, token }),
  ]);

  if (!userIsSuccess) {
    response.clearCookie(COOKIE_KEY_TOKEN, {
      path: '/',
      sameSite: 'lax',
    });
  }

  // 这里注意如果你设置了路由的 base，这里需要把 base 从 url 中删除掉
  await router.push(url);
  await router.isReady();

  const ctx: AppContext = {};
  const appHtml = await renderToString(app, ctx);

  if (ctx.redirect) {
    response.redirect(ctx.redirect);
    return false;
  }

  let title = router.currentRoute.value.meta.title as string || '';
  let description = router.currentRoute.value.meta.description as string || store.config.seo.description || store.config.description || '';
  let keywords = router.currentRoute.value.meta.keywords as string || store.config.seo.keywords || '';

  if (ctx.title) {
    title = ctx.title;
  }
  if (ctx.description) {
    description = ctx.description;
  }
  if (ctx.keywords) {
    keywords = ctx.keywords;
  }

  const preloadLinks = renderPreloadLinks(ctx.modules ?? [], manifest);

  return {
    preload: preloadLinks,
    app: appHtml,
    pinia: devalue(pinia.state.value),
    language: language.split('-')[0],
    title: getWrappedTitle(title, store.config.seo.title || store.config.name),
    description,
    keywords,
    userAgent,
    siteName: store.config.name,
    stats: store.config.stats,
    httpStatusCode: ctx.httpStatusCode || 200,
  };
}

function renderPreloadLinks(modules: string[], manifest: Record<string, string[]>) {
  let links = '';
  const seen = new Set<string>();

  modules.forEach((id) => {
    const files = manifest[id];

    if (files) {
      files.forEach((file) => {
        if (!seen.has(file)) {
          const filename = basename(file);

          seen.add(file);

          if (manifest[filename]) {
            for (const depFile of manifest[filename]) {
              links += renderPreloadLink(depFile);
              seen.add(depFile);
            }
          }

          links += renderPreloadLink(file);
        }
      });
    }
  });

  return links;
}

function renderPreloadLink(file: string) {
  if (file.endsWith('.js')) {
    return `<link rel="modulepreload" crossorigin href="${file}">`;
  }
  else if (file.endsWith('.css')) {
    return `<link rel="stylesheet" href="${file}">`;
  // } else if (file.endsWith('.woff')) {
  //   return ` <link rel="preload" href="${file}" as="font" type="font/woff" crossorigin>`
  // } else if (file.endsWith('.woff2')) {
  //   return ` <link rel="preload" href="${file}" as="font" type="font/woff2" crossorigin>`
  // } else if (file.endsWith('.gif')) {
  //   return ` <link rel="preload" href="${file}" as="image" type="image/gif">`
  // } else if (file.endsWith('.jpg') || file.endsWith('.jpeg')) {
  //   return ` <link rel="preload" href="${file}" as="image" type="image/jpeg">`
  // } else if (file.endsWith('.png')) {
  //   return ` <link rel="preload" href="${file}" as="image" type="image/png">`
  }
  else {
    // TODO
    return '';
  }
}
